﻿using System;
using System.Drawing;

namespace We.Moonlight.Geometry
{
    public struct Line
    {
        #region Line-Fields

        private double _slope;
        private int _yint;
        private int? _xint;

        #endregion
        #region Line-Properties

        public double Slope
        {
            get { return _slope; }
            set { _slope = value; }
        }
        public int YIntercept
        {
            get { return _yint; }
            set { _yint = value; }
        }
        public int? XIntercept
        {
            get { return _xint; }
            set { _xint = value; }
        }

        #endregion
        #region Line-Constructors

        public Line(Point P1, Point P2)
        {
            _slope = ((double)(P2.Y - P1.Y) / (double)(P2.X - P1.X)); _xint = null; _yint = (P1.Y - (int)Math.Round((_slope * P1.X), 0));

            if (Double.IsInfinity(_slope))
            {
                _xint = P1.X;
                _slope = 0;
                _yint = 0;
            }
        }
        public Line(double m, int b)
        {
            _xint = null;
            _yint = b;
            _slope = m;
        }
        public Line(int x)
        {
            _xint = x;
            _slope = 0;
            _yint = 0;
        }

        #endregion
        #region Line-Functions

        public double f(double x)
        {
            return (double)((_slope * x) + _yint);
        }
        public static bool IsBetweenOrOn(Line L1, Line L2, Point P)
        {
            if (L1.XIntercept == null && L2.XIntercept == null)
                //neither are vertical
                return (((L1.f(P.X) < P.Y) && (L2.f(P.X) > P.Y)) || ((L1.f(P.X) > P.Y) && (L2.f(P.X) < P.Y)));

            else if (L1.XIntercept != null && L2.XIntercept == null)
            {
                //L1 Vertical
                if (L2.Slope > 0)
                    return (((P.X < L1.XIntercept) && (L2.f(P.X) > P.Y)) || ((P.X > L1.XIntercept) && (L2.f(P.X) < P.Y)));
                else if (L2.Slope < 0)
                    return (((P.X > L1.XIntercept) && (L2.f(P.X) < P.Y)) || ((P.X < L1.XIntercept) && (L2.f(P.X) > P.Y)));
                else
                    throw new ArgumentOutOfRangeException("You cannot pass a Vertical and a Horizontal Line.", new ArithmeticException("Cannot Calculate If Point is between a Vertical and Horizontal line."));
            }

            else if (L1.XIntercept == null && L2.XIntercept != null)
            {
                //L2 Vertical

                if (L1.Slope > 0)
                    return (((P.X < L2.XIntercept) && (L1.f(P.X) > P.Y)) || ((P.X > L2.XIntercept) && (L1.f(P.X) < P.Y)));
                else if (L1.Slope < 0)
                    return (((P.X > L2.XIntercept) && (L1.f(P.X) < P.Y)) || ((P.X < L2.XIntercept) && (L1.f(P.X) > P.Y)));
                else
                    throw new ArgumentOutOfRangeException("You cannot pass a Vertical and a Horizontal Line.", new ArithmeticException("Cannot Calculate If Point is between a Vertical and Horizontal line."));
            }
            else
                //both are vertical
                return (((L1.XIntercept < P.X) && (L2.XIntercept > P.X)) || ((L1.XIntercept > P.X) && (L2.XIntercept < P.X)));
        }

        #endregion
        #region Line-Operators

        public static Line operator *(Line L1, Line L2)
        {
            //Schnittpunkt errechnen
            Point SP;
            if (L1.XIntercept == null && L2.XIntercept == null)
            {
                try
                {
                    SP = new Point((int)Math.Round((L1.YIntercept - L2.YIntercept) / (L2.Slope - L1.Slope), 0), (int)Math.Round(L1.f((L1.YIntercept - L2.YIntercept) / (L2.Slope - L1.Slope)), 0));
                    double mnew = L1.Slope * L2.Slope;
                    int tnew = (int)Math.Round((SP.Y - mnew * SP.X), 0);
                    return new Line(mnew, tnew);
                }
                catch (DivideByZeroException)
                {
                    if (L1.YIntercept == L2.YIntercept) return L1;
                    else return new Line(L1.Slope, (L1.YIntercept + L2.YIntercept) / 2);
                }
            }
            else if (L1.XIntercept != null && L2.XIntercept == null)
            {
                //L1 Vertical
                Point SP2 = new Point((int)L1.XIntercept, (int)Math.Round(L2.f((double)L1.XIntercept), 0));
                double mnew2 = 2 * L2.Slope;
                int tnew2 = (int)Math.Round((SP2.Y - mnew2 * SP2.X), 0);
                return new Line(mnew2, tnew2);
            }
            else if (L1.XIntercept == null && L2.XIntercept != null)
            {
                //L2 verticcal
                Point SP3 = new Point((int)L2.XIntercept, (int)Math.Round(L1.f((double)L2.XIntercept), 0));
                double mnew3 = 2 * L1.Slope;
                int tnew3 = (int)Math.Round((SP3.Y - mnew3 * SP3.X), 0);
                return new Line(mnew3, tnew3);
            }
            else
            {
                //both Vertical
                return new Line((int)Math.Round((decimal)((L1.XIntercept + L2.XIntercept) / 2), 0));
            }
        }
                #endregion
    }
}